﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VIA.ImageProcessing;
using VIA.Integration.Domain;
using VIA.Integration.Domain.Production;
using VIA.Project.Domain.Services;
using VIA.SiMoJi.Domain.Option;
using VIA.VTPlatformService;
using VT.Framework.Designer.Flow;
using VT.Framework.Scene2D.Structs;
using VT.Framework.Vision2D.VT2D;
using static VT.Framework.Utils.UtilMiniDump;
using static VT.Framework.Utils.UtilTaskbarProgress;

namespace VIA.SiMoJi.Domain.Vision
{
    public class ProductionLine : ProductionLine<Product>, IProductionLine
    {
        private readonly IVTPlatformManager _platformManager;
        private readonly IImageDetecter _detecter;
        private readonly IImageStorageService _imageStorageService;
        private readonly IFilePathBuilder _filePathBuilder;
        private readonly ILogger _logger;
        private readonly ICsvStorageService _csvStorageService;
        private readonly IDetectionContextFactory _detectionContextFactory;
        public event EventHandler<(string name, ImageDetectionResult result)> ImageDetectionResult;

        public ProductionLine(IServiceProvider service, IVTPlatformManager platformManager, IImageDetecter detecter, IImageStorageService imageStorageService, IFilePathBuilder filePathBuilder) : base(service)
        {
            _logger = service.GetRequiredService<ILogger<ProductionLine>>();
            _csvStorageService = service.GetRequiredService<ICsvStorageService>();
            _detectionContextFactory = service.GetRequiredService<IDetectionContextFactory>();
            this._platformManager = platformManager;
            this._detecter = detecter;
            this._imageStorageService = imageStorageService;
            this._filePathBuilder = filePathBuilder;
        }

        public async Task<TopImageAlgorithmResult> AddTopImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo, VisionStationOption option)
        {
            var context = _detectionContextFactory.Create(() =>
            {
                var result = _detecter.TopImageDetect(image, blockInfo);
                result.Offset = GetFeedTrayOffset(result.Pose, option.FeedTrayMarkPoint, option.TopCameraResolution);
                Quality quality = Math.Abs(result.Offset.X) <= option.MaxFeedTrayXOffset && Math.Abs(result.Offset.Y) <= option.MaxFeedTrayYOffset ? Quality.Ok : Quality.Ng;
                result.AddMessage($"顶部相机", Quality.Ok);
                result.AddMessage($"位置: {result.Pose:F3}mm", quality);
                result.AddMessage($"偏移: {result.Offset:F3}mm", quality);
                result.AddMessage($"放反: {(result.IsTrayReverseOK ? "OK" : "NG")}", result.IsTrayReverseOK ? Quality.Ok : Quality.Ng);
                return result;
            });
            var detectionResult = new ImageDetectionResult(image, context);
            ImageDetectionResult?.Invoke(this, ("0", detectionResult));
            await context.Task;
            _imageStorageService.Add(new ImageStoragePackage(Current, context.Task.Result, _filePathBuilder, [$"顶部相机"]));
            return context.Task.Result;
        }

        public async Task<TopImageAlgorithmResult> AddTopImageUnload(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo, VisionStationOption option)
        {
            var context = _detectionContextFactory.Create(() =>
            {
                var result = _detecter.TopImageDetect(image, blockInfo);
                result.Offset = GetFeedTrayOffset(result.Pose, option.FeedTrayMarkPoint, option.TopCameraResolution);
                Quality quality = Math.Abs(result.Offset.X) <= option.MaxFeedTrayXOffset && Math.Abs(result.Offset.Y) <= option.MaxFeedTrayYOffset ? Quality.Ok : Quality.Ng;
                result.AddMessage($"顶部相机下料", Quality.Ok);
                //result.AddMessage($"位置: {result.Pose:F3}mm", quality);
                //result.AddMessage($"偏移: {result.Offset:F3}mm", quality);
                //result.AddMessage($"放反: {(result.IsTrayReverseOK ? "OK" : "NG")}", result.IsTrayReverseOK ? Quality.Ok : Quality.Ng);
                return result;
            });
            var detectionResult = new ImageDetectionResult(image, context);
            ImageDetectionResult?.Invoke(this, ("10", detectionResult));
            await context.Task;
            _imageStorageService.Add(new ImageStoragePackage(Current, context.Task.Result, _filePathBuilder, [$"顶部相机下料"]));
            return context.Task.Result;
        }

        public async Task<TopImageAlgorithmResult> AddTopMarkImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo)
        {
            var context = _detectionContextFactory.Create(() =>
            {
                var result = _detecter.TopImageDetectMark(image, blockInfo);
                result.AddMessage($"顶部相机", Quality.Ok);
                result.AddMessage($"位置: {result.Pose:F4}mm", Quality.Ok);
                _imageStorageService.Add(new ImageStoragePackage(Current, result, _filePathBuilder, [$"顶部相机"]));
                return result;
            });
            var detectionResult = new ImageDetectionResult(image, context);
            ImageDetectionResult?.Invoke(this, ("0", detectionResult));
            await context.Task;
            return context.Task.Result;
        }
        public ConcurrentDictionary<int, string> BarcodeDictionaries => _barcodeDictionary;

        ConcurrentDictionary<int, string> _barcodeDictionary = new ConcurrentDictionary<int, string>();
        public async Task<BottomDuiWeiAlgorithmResult> AddBottomDuiWeiImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo, VisionStationOption option)
        {
            var result = new BottomDuiWeiAlgorithmResult(image);
            var context = _detectionContextFactory.Create(() =>
            {
                var result = _detecter.BottomImageDuiWei(image, blockInfo);
                var calData = productPart.IsUseSiMo1 ? option.NozzleCalibrationDatas[productPart.Jaw - 1] : option.NozzleCalibrationDatas2[productPart.Jaw - 1];
                Pose offset = new();
                if (option.DuiWeiMethod == DuiWeiMethod.XFirst)
                    offset = GetNozzleOffset2(result.Pose, calData, option.BottomCameraResolution, out Pose markPose, out Pose currentPose);
                else
                    offset = GetNozzleOffset(result.Pose, calData, option.BottomCameraResolution, out Pose markPose, out Pose currentPose);
                result.Offset = offset;
                Quality quality = Math.Abs(result.Offset.Angle) <= option.MaxNozzleAngleOffset && Math.Abs(result.Offset.X) <= option.MaxNozzleXOffset ? Quality.Ok : Quality.Ng;
                result.AddMessage($"下相机{productPart.Jaw}", Quality.Ok);
                string barcode = result.Barcode;
                if (!barcode.IsNullOrEmpty()) //读码不为空
                {
                    if (!barcode.Contains("NotBarcode"))//下相机读码
                    {
                        _barcodeDictionary[productPart.Jaw] = result.Barcode;
                        result.AddMessage($"条码: {result.Barcode}", Quality.Ok);
                    }
                    else //上相机读码
                    {
                        if (!_barcodeDictionary.TryGetValue(productPart.Jaw, out barcode))
                        {
                            result.Barcode = "FailCode";
                            result.AddMessage($"条码: {result.Barcode}", Quality.Ng);
                        }
                        else
                        {
                            result.Barcode = barcode;
                            result.AddMessage($"条码: {result.Barcode}", Quality.Ok);
                        }
                    }
                }
                else
                {
                    result.Barcode = "FailCode";
                    result.AddMessage($"条码: {result.Barcode}", Quality.Ng);
                }
                //if (!result.Barcode.IsNullOrEmpty() && !result.Barcode.Contains("NotBarcode"))//读码为空，不下相机读码
                //{
                //    result.AddMessage($"条码: {result.Barcode}", Quality.Ok);
                //}
                result.AddMessage($"图像位置: {result.Pose:F3}", Quality.Ok);
                result.AddMessage($"基准位置: {calData.MarkPoint:F3}", Quality.Ok);
                //result.AddMessage($"当前位置1: {currentPose:F4}mm", Quality.Ok);
                //result.AddMessage($"基准位置1: {markPose:F4}mm", Quality.Ok);
                result.AddMessage($"偏移: {offset:F3}", quality);
                //string cameraName = productPart.IsUseSiMo1 ? "下相机撕膜1-扫码" : "下相机撕膜2-扫码";
                //_imageStorageService.Add(new ImageStoragePackage(Current, result, _filePathBuilder, [cameraName, $"{productPart.Col}_{productPart.Jaw}"]));
                //if (!result.Barcode.IsNullOrEmpty() && !result.Barcode.Contains("NotBarcode"))
                //{
                //    _barcodeDictionary.TryAdd(productPart.Jaw, result.Barcode);
                //}
                return result;
            });
            var detectionResult = new ImageDetectionResult(image, context);
            ImageDetectionResult?.Invoke(this, ($"{productPart.Jaw}", detectionResult));
            await context.Task;
            string cameraName = productPart.IsUseSiMo1 ? "下相机撕膜1-扫码" : "下相机撕膜2-扫码";
            _imageStorageService.Add(new ImageStoragePackage(Current, context.Task.Result, _filePathBuilder, [cameraName, context.Task.Result.Barcode, $"{productPart.Col}_{productPart.Jaw}"]));
            return context.Task.Result;
        }

        public async Task<BottomDetectAlgorithmResult> AddBottomDetectImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo)
        {
            var context = _detectionContextFactory.Create(() =>
            {
                var result = _detecter.BottomImageDetect(image, blockInfo);
                result.AddMessage($"下相机{productPart.Jaw}", Quality.Ok);
                result.AddMessage($"撕膜结果", result.IsSiMoOK ? Quality.Ok : Quality.Ng);
                string barcode = string.Empty;
                if (!_barcodeDictionary.TryGetValue(productPart.Jaw, out barcode))
                    barcode = "FailCode";
                result.Barcode = barcode;
                //string cameraName = productPart.IsUseSiMo1 ? "下相机撕膜1-复检" : "下相机撕膜2-复检";
                //_imageStorageService.Add(new ImageStoragePackage(Current, result, _filePathBuilder, [cameraName, $"{productPart.Col}_{productPart.Jaw}"]));
                return result;
            });
            var detectionResult = new ImageDetectionResult(image, context);
            ImageDetectionResult?.Invoke(this, ($"{productPart.Jaw}", detectionResult));
            await context.Task;
            string cameraName = productPart.IsUseSiMo1 ? "下相机撕膜1-复检" : "下相机撕膜2-复检";
            _imageStorageService.Add(new ImageStoragePackage(Current, context.Task.Result, _filePathBuilder, [cameraName, context.Task.Result.Barcode, $"{productPart.Col}_{productPart.Jaw}"]));
            return context.Task.Result;
        }

        public async Task<BottomDuiWeiAlgorithmResult> AddBottomMarkOrCalibrationImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo)
        {
            var context = _detectionContextFactory.Create(() =>
            {
                var result = _detecter.BottomImageDuiWei(image, blockInfo);
                result.AddMessage($"下相机{productPart.Jaw}", Quality.Ok);
                result.AddMessage($"位置{result.Pose}", Quality.Ok);
                string cameraName = productPart.IsUseSiMo1 ? "下相机撕膜1" : "下相机撕膜2";
                _imageStorageService.Add(new ImageStoragePackage(Current, result, _filePathBuilder, [cameraName, $"{productPart.Col}_{productPart.Jaw}"]));
                return result;
            });
            var detectionResult = new ImageDetectionResult(image, context);
            ImageDetectionResult?.Invoke(this, ($"{productPart.Jaw}", detectionResult));
            await context.Task;
            return context.Task.Result;
        }

        public async Task<UpImageAlgorithmResult> AddUpImage(ImageData image1, ProductPart productPart, VTFlowBlockInfo blockInfo)
        {
            var image = image1.Rotate(-90);
            var result = new UpImageAlgorithmResult(image);
            var context = _detectionContextFactory.Create(() =>
            {
                result = _detecter.UpImageDetect(image, blockInfo);
                result.AddMessage($"上相机{productPart.Jaw}", Quality.Ok);
                _barcodeDictionary.TryAdd(productPart.Jaw, result.Barcode);
                return result;
            });
            var detectionResult = new ImageDetectionResult(image, context);
            ImageDetectionResult?.Invoke(this, ($"{productPart.Jaw}", detectionResult));
            await context.Task;
            string cameraName = "上相机读码";
            _imageStorageService.Add(new ImageStoragePackage(Current, context.Task.Result, _filePathBuilder, [cameraName, context.Task.Result.Barcode, $"{productPart.Col}_{productPart.Jaw}"]));
            return context.Task.Result;
        }

        public Pose GetFeedTrayOffset(Pose pose, Pose feedTrayMarkPoint, double resolution)
        {
            Pose deltP = feedTrayMarkPoint - pose;
            return new Pose(deltP.X * resolution, deltP.Y * resolution, deltP.Angle);
        }

        public Pose GetFeedTrayOffset2(Pose pose, Pose feedTrayMarkPoint, CMatrix matrix)
        {
            Pose newPose = matrix.Transform(pose);
            Pose newMarkPose = matrix.Transform(feedTrayMarkPoint);
            Pose deltP = newMarkPose - newPose;
            return deltP;
        }

        /// <summary>
        /// 对角度
        /// </summary>
        /// <param name="pose"></param>
        /// <param name="nozzle"></param>
        /// <param name="resolution"></param>
        /// <returns></returns>
        public Pose GetNozzleOffset(Pose pose, NozzleCalibrationData nozzle, double resolution, out Pose markPos, out Pose currentPos)
        {
            currentPos = new Pose(pose.X - nozzle.RotationCenter.X, pose.Y - nozzle.RotationCenter.Y, pose.Angle);
            markPos = new Pose(nozzle.MarkPoint.X - nozzle.RotationCenter.X, nozzle.MarkPoint.Y - nozzle.RotationCenter.Y, pose.Angle);
            double angle = nozzle.MarkPoint.Angle - currentPos.Angle;
            var newPos = currentPos.Rotate(angle);
            double deltY = newPos.Y - markPos.Y;
            return new Pose(-deltY * resolution, 0, angle);
        }

        /// <summary>
        /// 对X方向
        /// </summary>
        /// <param name="index"></param>
        /// <param name="pose"></param>
        /// <param name="nozzle"></param>
        /// <param name="resolution"></param>
        /// <returns></returns>
        public Pose GetNozzleOffset2(Pose pose, NozzleCalibrationData nozzle, double resolution, out Pose markPos, out Pose currentPos)
        {
            currentPos = new Pose(pose.X - nozzle.RotationCenter.X, pose.Y - nozzle.RotationCenter.Y, pose.Angle);
            markPos = new Pose(nozzle.MarkPoint.X - nozzle.RotationCenter.X, nozzle.MarkPoint.Y - nozzle.RotationCenter.Y, pose.Angle);
            double currentRadius = Math.Sqrt(currentPos.X * currentPos.X + currentPos.Y * currentPos.Y);
            double currentAngle = Math.Acos(currentPos.X / currentRadius);
            if (currentPos.Y < 0) currentAngle = -currentAngle;
            double markAngle = Math.Acos(markPos.X / currentRadius);
            if (markPos.Y < 0) markAngle = -markAngle;
            double angle = (markAngle - currentAngle) * 180 / Math.PI;
            double newY = Math.Abs(currentRadius * Math.Sin(markAngle));
            if (markPos.Y < 0)
            {
                newY = -newY;
            }
            return new Pose((markPos.Y - newY) * resolution, 0, angle);
        }

        public void OnImageDetectionResult(string name, ImageDetectionResult result)
        {
            ImageDetectionResult?.Invoke(this, (name, result));
        }
    }

    public interface IProductionLine : IProductionLine<Product>
    {
        ConcurrentDictionary<int, string> BarcodeDictionaries { get; }
        Task<TopImageAlgorithmResult> AddTopImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo, VisionStationOption option);
        Task<TopImageAlgorithmResult> AddTopImageUnload(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo, VisionStationOption option);
        Task<TopImageAlgorithmResult> AddTopMarkImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo);
        Task<BottomDuiWeiAlgorithmResult> AddBottomDuiWeiImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo, VisionStationOption option);
        Task<BottomDetectAlgorithmResult> AddBottomDetectImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo);
        Task<BottomDuiWeiAlgorithmResult> AddBottomMarkOrCalibrationImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo);
        Task<UpImageAlgorithmResult> AddUpImage(ImageData image, ProductPart productPart, VTFlowBlockInfo blockInfo);
        event EventHandler<(string name, ImageDetectionResult result)> ImageDetectionResult;
        void OnImageDetectionResult(string name, ImageDetectionResult result);
    }
}
